package org.pi.common.matrix

trait MatrixApi extends org.pi.common.matrix.range.IntRangeApi {
	// =====================
	// default matrices
	// =====================
	def rand(dims: Int*) = {
	  	val res = FloatMatrix(dims :_*)
    	val rand = new java.util.Random()
    	for (index <- 0 until res.length) res(index) = rand.nextFloat()
    	res
  	}
  	def ones(dims: Int*) = {
	  	//val res = new Matrix[Float](dims : _*)
	  	val res = FloatMatrix(dims:_*)
    	for (index <- 0 until res.length) res(index) = 1
    	res
  	}
  	def zeros(dims: Int*) = {
	  	val res = FloatMatrix(dims:_*)
	  	for (index <- 0 until res.length) res(index) = 0
	  	res
  	}
  	//def empty(dims: Int*) = new Matrix[Float](dims : _*)
  	def empty(dims: Int*) = FloatMatrix(dims:_*)

  	// =====================
  	// max
  	// =====================
  	//def max(a: Float, b: Float) : Float = if (a>b) a else b
  	def max(a: FloatMatrix) : Float = {
  		//a.foldLeft(a(0))(max)
  		var result: Float = a(0)
  		for (index <- 1 until a.length) result = Math.max(result, a(index))
  		result
  	}
  
  	// =====================
  	// min
  	// =====================
  	//def min(a: Float, b: Float) : Float = if (a<b) a else b
  	def min(a: FloatMatrix) : Float = {
  		// a.foldLeft(a(0))(min)
  		var result: Float = a(0)
  		for (index <- 1 until a.length) result = Math.min(result, a(index))
  		result
  	}
  
  	// =====================
  	// size
  	// =====================
  	def size(a: Matrix[Float]) : List[Int] = a.shape.toList
  	def size(a: Matrix[Float], dim: Int) : Int = a.length(dim)

  	// =====================
  	// tools
  	// =====================
  	def linspace(start: Float, end:Float, length:Int): FloatMatrix = {
	  val m = FloatMatrix(length)
		for (k <- 0 until length) {
			m(k) = start + k.toFloat*(end-start)/((length-1).toFloat)
		}
		m
  	}
  	
  	def meshgrid(x:FloatMatrix, y:FloatMatrix): (FloatMatrix,FloatMatrix) = {
  		if (x.shape.size!=1) throw new Error("x must be 1d")
  		if (y.shape.size!=1) throw new Error("y must be 1d")
  		val X = empty(y.length, x.length);
  		val Y = empty(y.length, x.length);
  		var iy:Int =0
  		while(iy<y.length) {
  			var ix:Int =0
  			while(ix<x.length) {
  				X(iy,ix) = x(ix)
  				Y(iy,ix) = y(iy)
  				ix+=1
  			}
  			iy+=1
  		}
  		(X,Y)
  	}

  	// =====================
  	// math ops
  	// =====================
  	def applyFunc1(a: FloatMatrix, f: (Float)=>Float) : FloatMatrix = {
  		//val m = new Matrix[Float]( a.shape :_* )
  		val m = FloatMatrix( a.shape :_*)
  		val length =  a.length
  		for (k <- 0 until length) {
  			m(k) = f(a(k))
  		}
  		m
  	}
  	def applyDoubleFunc1(a: FloatMatrix, f: (Double)=>Double) : FloatMatrix = {
  		//val m = new Matrix[Float]( a.shape :_* )
  		val m = FloatMatrix( a.shape :_*)
  		val length =  a.length
  		for (k <- 0 until length) {
  			m(k) = f(a(k).toDouble).toFloat
  		}
  		m
  	}
  
  	def sin(a: FloatMatrix) = applyDoubleFunc1(a, Math.sin)
  	def cos(a: FloatMatrix) = applyDoubleFunc1(a, Math.cos)
  	//def atan2(a: Matrix[Float]) = applyDoubleFunc1(a, Math.atan2)
  	def sqrt(a: FloatMatrix) = applyDoubleFunc1(a, Math.sqrt)
  
  	def pi = java.lang.Math.PI.toFloat

  	// =====================
  	// matrix ops
  	// =====================
  	def any(m:Matrix[Boolean]) = m.foldLeft(false)( (a,b)=>a|b ) 
  	def all(m:Matrix[Boolean]) = m.foldLeft(true)( (a,b)=>a&b ) 
    
	implicit def floatListToMatrix(lst: List[Float]) : FloatMatrix = FloatMatrix(lst)

	implicit def intListToMatrix(lst: List[Int]) : FloatMatrix = {
		val m = FloatMatrix(lst.length)
		for(k <- 0 until lst.length) m(k) = lst(k).toFloat
		m
  	}
  
	implicit def convertIntMatrixToIntSelection(mParam: IntMatrix) : IntSelection = new IntSelection {
	  val m = mParam;
	  override def toIntMatrix() : IntMatrix = m
	  override def toIntMatrixForGivenStartEndIndices(startIndex: Int, endIndex: Int) : IntMatrix = m
	}
  
	implicit def convertFloatSubMatrixToFloatMatrix(m: FloatIndexedSubMatrix) : FloatMatrix = m.copy()
	implicit def convertIntSubMatrixToIntMatrix(m: IntIndexedSubMatrix) : IntMatrix = m.copy()
	implicit def convertBooleanSubMatrixToBooleanMatrix(m: BooleanIndexedSubMatrix) : BooleanMatrix = m.copy()

	implicit def convertIntSubMatrixToFloatMatrix(m: IntIndexedSubMatrix) : FloatMatrix = m.toFloatMatrix()
	implicit def convertBooleanSubMatrixToFloatMatrix(m: BooleanIndexedSubMatrix) : FloatMatrix = m.toFloatMatrix()

	implicit def convertIntMatrixToFloatMatrix(m: IntMatrix) : FloatMatrix = m.toFloatMatrix()
	implicit def convertBooleanMatrixToFloatMatrix(m: BooleanMatrix) : FloatMatrix = m.toFloatMatrix()
}

object Api extends MatrixApi {
}
